#include "e2d/core/Scene.h"
#include "e2d/core/Entity.h"

std::vector<std::shared_ptr<e2d::Entity>> e2d::Scene::ExistEntities() const
{
	std::vector<std::shared_ptr<e2d::Entity>> res;
	for (auto e : m_Entities)
	{
		if (e->m_Exist)
		{
			res.push_back(e);
		}
	}
	return res;
}

void e2d::Scene::RegisterEntity(std::shared_ptr<e2d::Entity> entity)
{
	m_EntityMap[entity->m_UUID] = entity;
}

std::shared_ptr<e2d::Entity> e2d::Scene::NewEntity(const std::string& newEntityName)
{
	auto inst = std::make_shared<Entity>(rg.create(), this, newEntityName);
	//inst->GetOrEmplace<e2d::TransformNode>();
	//inst->GetOrEmplace<e2d::MonoScripts>();
	return inst;
}

std::shared_ptr<e2d::Entity> e2d::Scene::NewEntityOnParent(e2d::Entity* parent)
{
	auto inst = NewEntity();

	inst->m_Parent = parent;
	inst->m_Scene = this;

	AddEntityToParent(inst, parent);

	return inst;
}

bool e2d::Scene::HasEntity(const std::string& uid) const
{
	return m_EntityMap.find(uid) != m_EntityMap.end();
}

void e2d::Scene::AddEntityToParent(std::shared_ptr<e2d::Entity> child, e2d::Entity* parent)
{
	if (parent == 0)
	{
		m_Entities.emplace_back(child);
		UploadEntityMap();
	}
	else
	{
		parent->m_Children.emplace_back(child);
		parent->UploadChildrenMap();
	}

}

e2d::Scene::Scene() :Object(), m_Name("SampleScene1") {

	m_Entities.clear();
	m_EntityMap.clear();
	m_AllEntitiesCalculated.clear();

};
e2d::Scene::~Scene() {

	rg.clear();
}

std::shared_ptr<e2d::Entity> e2d::Scene::CreateEntityFrom(const std::string& dir, const std::function<void(e2d::Entity*, const std::string&)>& func)
{

	fs::path _p(dir);
	std::string fileName = _p.filename().string();

	auto res = NewEntity();
	res->m_Name = fileName;
	for (const auto it : fs::directory_iterator(dir))
	{
		if (fs::is_directory(it))
		{
			auto child = CreateEntityFrom(it.path().string(), func);
			res->AddChild(child);
		}
		else
		{
			auto child = res->NewChild();
			child->m_Name = it.path().filename().string();

			if (func)
			{
				func(child.get(), it.path().relative_path().string());

			}
		}

	}
	UploadEntityMap();
	return res;
}


void e2d::Scene::OnLoad()
{
	UploadEntityMap();

	m_EditingEntity.LoadFromScene(this);
	m_EditingCamera.LoadFromScene(this);
	m_MainCamera.LoadFromScene(this);

}


void e2d::Scene::UploadEntityMap()
{
	ztclog::info("upload entity map and build hieracy relationship");

	m_EntityMap.clear();
	m_AllEntitiesCalculated.clear();
	for (auto e : m_Entities)
	{
		ProcessAllEntities(m_AllEntitiesCalculated, e);
	}

	for (auto e : m_AllEntitiesCalculated)
	{
		RegisterEntity(e);
	}
	for (auto e : m_AllEntitiesCalculated)
	{
		//if (!e->HasComponent<e2d::Prefeb>())
		//{
		//	m_AllEntitiesCalculatedNoPrefeb.push_back(e);
		//}
	}

}

void e2d::Scene::ProcessAllEntities(std::vector<std::shared_ptr<e2d::Entity>>& target, std::shared_ptr<e2d::Entity> e)
{
	target.emplace_back(e);

	for (auto child : e->m_Children)
	{
		child->m_Scene = this;
		child->m_Parent = e.get();
		child->UploadChildrenMap();
		ProcessAllEntities(target, child);
	}
}

bool e2d::Scene::RemoveChidren(const std::vector<std::string>& uuids)
{
	std::vector<std::shared_ptr<e2d::Entity>> newEntityList;

	int index = 0;

	for (auto child : m_Entities)
	{
		if(UUIDsContain(uuids, child->m_UUID))
		{
			index++;
		}
		else
		{
			newEntityList.push_back(child);
		}
	}
	if (index > 0)
	{

		ztclog::info("num of entities which should be removed is %d", index);
		this->m_Entities = newEntityList;
	}
	else
	{
		ztclog::info("no entities should be removed");
	}
	return index > 0;
}

void e2d::Scene::SetAllExist()
{
	for (auto it : m_AllEntitiesCalculated)
	{
		it->m_Exist = true;
	}
	for (auto it : m_AllEntitiesCalculatedNoPrefeb)
	{
		it->m_Exist = true;
	}
}

bool e2d::Scene::SnatchEntityAsChild(const std::string& uuid)
{
	if (!HasEntity(uuid))
	{
		ztclog::info("scene not has the uuid %s", uuid.c_str());
		return false;
	}
	auto entity = GetEntity(uuid);
	
	return SnatchEntityInstAsChild(entity);
}

bool e2d::Scene::SnatchEntityInstAsChild( std::shared_ptr<e2d::Entity> entity)
{
	entity->DisappearFromParentChildren();
	this->m_Entities.push_back(entity);

	entity->m_Parent = 0;
	entity->m_Scene = this;
	
	return true;

}
